/*
 * Copyright (c) 2024 endless-sky
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "auth_processor.h"
#include "utils_log.h"
#include "common_user_manager.h"
#include "utils_string_ex.h"
#include <chrono>
#include <random>
#include <openssl/evp.h>
#include "web_server.h"

namespace El {
namespace Protocol {

AuthProcessor::~AuthProcessor()
{
    // 如果有活跃的access_token，从IHttpServer中移除
    if (!current_access_token_.empty()) {
        El::WebServer::IHttpServer::GetInstance().RemoveBearerToken(current_access_token_);
    }
}

void AuthProcessor::RegisterMethodHandlers(
    std::unordered_map<std::string, std::function<nlohmann::json(const std::string &, const nlohmann::json &)>>
        &method_handlers)
{
    // 注册认证相关方法处理器
    method_handlers["auth.getChallenge"] = [this](const std::string &id, const nlohmann::json &params) {
        return HandleAuthGetChallenge(id, params);
    };

    method_handlers["auth.login"] = [this](const std::string &id, const nlohmann::json &params) {
        return HandleAuthLogin(id, params);
    };

    method_handlers["auth.refreshToken"] = [this](const std::string &id, const nlohmann::json &params) {
        return HandleAuthRefreshToken(id, params);
    };

    method_handlers["auth.logout"] = [this](const std::string &id, const nlohmann::json &params) {
        return HandleAuthLogout(id, params);
    };

    method_handlers["auth.getSessions"] = [this](const std::string &id, const nlohmann::json &params) {
        return HandleAuthGetSessions(id, params);
    };
}

nlohmann::json AuthProcessor::HandleAuthGetChallenge(const std::string &id, const nlohmann::json &params)
{
    // 检查参数
    if (!params.contains("username") || !params["username"].is_string()) {
        return CreateErrorResponse(id, 400, "Bad Request", "Missing or invalid username");
    }

    std::string username = params["username"];
    std::string device_id;

    if (params.contains("device_id") && params["device_id"].is_string()) {
        device_id = params["device_id"];
    }

    // 生成随机挑战码
    std::string challenge = Utils::GenerateToken(32);

    // 存储挑战码信息
    last_challenge_.challenge = challenge;
    last_challenge_.username = username;
    last_challenge_.device_id = device_id;
    last_challenge_.create_time = std::chrono::system_clock::now();
    last_challenge_.used = false;

    nlohmann::json result = {{"challenge", challenge}, {"expires_in", CHALLENGE_EXPIRE_SECONDS}};
    return CreateSuccessResponse(id, result);
}

nlohmann::json AuthProcessor::HandleAuthLogin(const std::string &id, const nlohmann::json &params)
{
    EL_DEBUG("Auth login request received");

    // 检查必需参数
    if (!params.contains("username") || !params["username"].is_string() || !params.contains("credential") ||
        !params["credential"].is_string() || !params.contains("challenge") || !params["challenge"].is_string()) {
        return CreateErrorResponse(id, 400, "Bad Request", "Missing required parameters");
    }

    std::string username = params["username"];
    std::string credential = params["credential"];
    std::string challenge = params["challenge"];
    std::string device_id;

    if (params.contains("device_id") && params["device_id"].is_string()) {
        device_id = params["device_id"];
    }

    // 验证挑战码
    if (!ValidateChallenge(username, challenge, device_id)) {
        return CreateErrorResponse(id, 401, "Unauthorized", "Invalid or expired challenge");
    }

    // 获取用户信息并验证
    El::Common::UserInfo user_info;
    if (!El::Common::UserManager::GetInstance().GetUserInfo(username, user_info)) {
        return CreateErrorResponse(id, 401, "Unauthorized", "Invalid username or password");
    }

    // 检查用户是否启用
    if (!user_info.enabled) {
        return CreateErrorResponse(id, 403, "Forbidden", "User account is disabled");
    }

    // 验证密码
    std::string expected_credential = SHA256(user_info.password + challenge);
    if (expected_credential != credential) {
        return CreateErrorResponse(id, 401, "Unauthorized", "Invalid username or password");
    }

    // 生成访问令牌和刷新令牌
    std::string access_token = Utils::GenerateToken(64);  // 生成64位随机access_token
    std::string refresh_token = Utils::GenerateToken(64); // 生成64位随机refresh_token

    nlohmann::json user = {
        {"id", "user-" + username},
        {"username", username},
        {"role", user_info.role},
        {"permissions", nlohmann::json::array({"camera.view", "camera.control"})},
        {"last_login", user_info.lastLoginTime},
        {"last_login_ip", "192.168.1.100"} // TODO: 获取实际的客户端 IP
    };

    nlohmann::json result = {{"access_token", access_token},
                             {"refresh_token", refresh_token},
                             {"token_type", "Bearer"},
                             {"expires_in", 3600},
                             {"user", user}};

    // 保存当前的access_token并添加到IHttpServer
    current_access_token_ = access_token;
    El::WebServer::IHttpServer::GetInstance().AddBearerToken(access_token);

    return CreateSuccessResponse(id, result);
}

bool AuthProcessor::ValidateChallenge(const std::string &username,
                                      const std::string &challenge,
                                      const std::string &device_id)
{
    // 检查是否已过期
    if (IsExpired()) {
        EL_ERROR("Challenge expired");
        return false;
    }

    // 检查挑战码是否匹配
    if (last_challenge_.challenge != challenge) {
        EL_ERROR("Invalid challenge");
        return false;
    }

    // 检查是否已使用
    if (last_challenge_.used) {
        EL_ERROR("Challenge already used");
        return false;
    }

    // 检查用户名是否匹配
    if (last_challenge_.username != username) {
        EL_ERROR("Challenge username mismatch: {} != {}", last_challenge_.username, username);
        return false;
    }

    // 检查设备ID是否匹配（如果有）
    if (!device_id.empty() && !last_challenge_.device_id.empty() && last_challenge_.device_id != device_id) {
        EL_ERROR("Challenge device_id mismatch: {} != {}", last_challenge_.device_id, device_id);
        return false;
    }

    // 标记挑战码为已使用
    last_challenge_.used = true;
    return true;
}

bool AuthProcessor::IsExpired() const
{
    auto now = std::chrono::system_clock::now();
    auto age = std::chrono::duration_cast<std::chrono::seconds>(now - last_challenge_.create_time).count();
    return age >= CHALLENGE_EXPIRE_SECONDS || last_challenge_.challenge.empty();
}

nlohmann::json AuthProcessor::HandleAuthRefreshToken(const std::string &id, const nlohmann::json &params)
{
    if (!params.contains("refresh_token") || !params["refresh_token"].is_string()) {
        return CreateErrorResponse(id, 400, "Bad Request", "Missing refresh_token");
    }

    // 生成新的access_token
    std::string new_access_token = Utils::GenerateToken(64);

    // 在IHttpServer中更新token
    if (!current_access_token_.empty()) {
        El::WebServer::IHttpServer::GetInstance().RemoveBearerToken(current_access_token_);
    }
    current_access_token_ = new_access_token;
    El::WebServer::IHttpServer::GetInstance().AddBearerToken(new_access_token);

    nlohmann::json result = {{"access_token", new_access_token}, {"token_type", "Bearer"}, {"expires_in", 3600}};

    return CreateSuccessResponse(id, result);
}

nlohmann::json AuthProcessor::HandleAuthLogout(const std::string &id, const nlohmann::json &params)
{
    std::string device_id;
    bool logout_all = false;

    if (params.contains("device_id") && params["device_id"].is_string()) {
        device_id = params["device_id"];
    }

    if (params.contains("logout_all") && params["logout_all"].is_boolean()) {
        logout_all = params["logout_all"];
    }

    // 从IHttpServer中移除access_token
    if (!current_access_token_.empty()) {
        El::WebServer::IHttpServer::GetInstance().RemoveBearerToken(current_access_token_);
        current_access_token_.clear();
    }

    // TODO: 实际登出处理逻辑

    nlohmann::json result;
    if (logout_all) {
        result["logged_out_devices"] = nlohmann::json::array({"device_id1", "device_id2"});
    } else {
        result["logged_out_devices"] = nlohmann::json::array({device_id});
    }

    return CreateSuccessResponse(id, result);
}

nlohmann::json AuthProcessor::HandleAuthGetSessions(const std::string &id, const nlohmann::json &params)
{
    (void)params;
    // 返回示例会话数据
    nlohmann::json session1 = {{"device_id", "unique_device_id1"},
                               {"device_info", {{"type", "browser"}, {"os", "Windows"}, {"client", "Chrome 120.0.0"}}},
                               {"ip", "192.168.1.100"},
                               {"login_time", "2024-03-20T02:00:00Z"},
                               {"last_active", "2024-03-20T02:30:00Z"},
                               {"is_current", true}};

    nlohmann::json session2 = {{"device_id", "unique_device_id2"},
                               {"device_info", {{"type", "mobile"}, {"os", "iOS"}, {"client", "App 1.0.0"}}},
                               {"ip", "192.168.1.101"},
                               {"login_time", "2024-03-19T10:00:00Z"},
                               {"last_active", "2024-03-20T01:30:00Z"},
                               {"is_current", false}};

    nlohmann::json result = {{"total", 2}, {"sessions", nlohmann::json::array({session1, session2})}};

    return CreateSuccessResponse(id, result);
}

std::string AuthProcessor::SHA256(const std::string &input)
{
    EVP_MD_CTX *mdctx = EVP_MD_CTX_new();
    if (mdctx == nullptr) {
        EL_ERROR("Failed to create EVP_MD_CTX");
        return "";
    }

    if (EVP_DigestInit_ex(mdctx, EVP_sha256(), nullptr) != 1) {
        EL_ERROR("Failed to initialize SHA256 digest");
        EVP_MD_CTX_free(mdctx);
        return "";
    }

    if (EVP_DigestUpdate(mdctx, input.c_str(), input.size()) != 1) {
        EL_ERROR("Failed to update SHA256 digest");
        EVP_MD_CTX_free(mdctx);
        return "";
    }

    unsigned char hash[EVP_MAX_MD_SIZE];
    unsigned int hash_len = 0;
    if (EVP_DigestFinal_ex(mdctx, hash, &hash_len) != 1) {
        EL_ERROR("Failed to finalize SHA256 digest");
        EVP_MD_CTX_free(mdctx);
        return "";
    }

    EVP_MD_CTX_free(mdctx);

    char buf[EVP_MAX_MD_SIZE * 2 + 1];
    for (unsigned int i = 0; i < hash_len; ++i) {
        sprintf(&buf[i * 2], "%02x", hash[i]);
    }
    buf[hash_len * 2] = '\0';

    return std::string(buf);
}

} // namespace Protocol
} // namespace El